<?php

App::uses('AppModel', 'Model');
App::uses('CompositeKeyModel', 'Model');

/**
 * MemberAccount Model
 *
 * @property Member $Member
 * @property MemberAccountSession $MemberAccountSession Account Session Model Reference
 */
class MemberAccount extends CompositeKeyModel {


    public $useDbConfig = "organisations";
    public $primaryKeys = array('id', 'member_id');

    /**
     * belongsTo associations
     *
     * @var array
     */
    public $belongsTo = array(
        'Member' => array(
            'className' => 'Member',
            'foreignKey' => 'member_id',
            'conditions' => '',
            'fields' => '',
            'order' => ''
        )
    );
    
    /**
     * belongsTo associations
     *
     * @var array
     */
    public $hasMany = array(
        'MemberAccountSession' => array(
            'className' => 'MemberAccountSession',
            'foreignKey' => 'member_account_id',
            'conditions' => '',
            'fields' => '',
            'order' => ''
        ),
        'OrganisationAccount' => array(
            'className' => 'OrganisationAccount',
            'foreignKey' => 'member_account_id',
            'conditions' => '',
            'fields' => '',
            'order' => ''
        ),
    );

    /**
     * 
     * @param string $username
     * @param string $password
     * @param boolean $remember_me
     * @return array|boolean
     */
    public function attempt_login($username, $password, $remember_me = false) {
        $accounts = $this->find('all', array(
            'contain' => array(
                'Member' => array(
                    'limit' => 1,
                    'MemberImage' => array(
                        'order' => array('id' => 'desc'),
                        'limit' => 1
                    )
                ),
                'OrganisationAccount' => array('Organisation')
            ),
            'conditions' => array(
                'MemberAccount.username' => $username,
                'MemberAccount.active' => 1,
                'MemberAccount.deleted' => 0
            )
        ));

        foreach ($accounts as $account) {
            if ($account['MemberAccount']['password'] == md5($password . $account['MemberAccount']['pass_salt'])) {
                
                $session = $this->MemberAccountSession->start($account['MemberAccount']['id'], $remember_me);
                
                // remove sensitive information
                unset($account['MemberAccount']['password']);
                unset($account['MemberAccount']['pass_salt']);
                
                return array('account' => $account, 'session' => $session);
            }
        }

        return false;
    }


    public function clef_login($username) {
        $account = $this->find('first', array(
            'contain' => array(
                'Member' => array(
                    'limit' => 1,
                    'MemberImage' => array(
                        'order' => array('id' => 'desc'),
                        'limit' => 1
                    )
                ),
                'OrganisationAccount' => array('Organisation')
            ),
            'conditions' => array(
                'MemberAccount.username' => $username,
                'MemberAccount.active' => 1,
                'MemberAccount.deleted' => 0
            )
        ));

        if($account){
            $session = $this->MemberAccountSession->start($account['MemberAccount']['id'], false);

            // remove sensitive information
            unset($account['MemberAccount']['password']);
            unset($account['MemberAccount']['pass_salt']);

            return array('account' => $account, 'session' => $session);
        }
        else{
            return false;
        }
    }
    
    /**
     * Returns a member account. Parameter $id can be either a username or member_id
     * @param string $id
     * @return array
     */
    public function getMemberAccount($id) {
        $account = $this->find('first', array(
            'contain' => array(
                'Member' => array(
                    'fields' => array(
                        'id', 'first_name', 'last_name', 'dob', 'email'
                    )
                )
            ),
            'fields' => array('id', 'username', 'member_id', 'timezone', 'last_login', 'created', 'active'),
            'conditions' => array(
                'or' => array(
                    'MemberAccount.username' => $id,
                    'MemberAccount.member_id' => $id
                ),
                'MemberAccount.deleted' => 0
            )
        ));
        
        if( !$account ) {
            return false;
        }
        
        $check_hash = sha1($account['MemberAccount']['username'] . $account['MemberAccount']['id'] . $account['Member']['first_name']);
        
        return array('account' => $account, 'check_hash' => $check_hash);
    }

    /**
     * 
     * @param array $data
     * @return array
     */
    public function createAccount($data) {
        if( !isset($data['username']) || !isset($data['password']) ) {
            return array('status' => 'failed', 'message' => 'Invalid parameters passed.');
        } 
        
        $account_exists = $this->find('count', array(
            'conditions' => array(
                'MemberAccount.username' => $data['username'],
                'MemberAccount.deleted' => 0
            )
        ));
        
        if( $account_exists ) {
            return array('status' => 'failed', 'message' => 'A membership account with the existing username already exists.');
        }
        
        // generate account data
        $pass_salt = rand(10000, 99999);
        
        $account = array(
            'username' => $data['username'],
            'pass_salt' => $pass_salt,
            'password' => md5($data['password'] . $pass_salt),
            'active' => 0, // activation would be required
            'timezone' => isset($data['timezone']) ? $data['timezone'] : 'UTC'
        );
        
        $member = $this->Member->find('first', array(
            'conditions' => array(
                'Member.email' => $data['username'],
                'Member.gender' => $data['gender'],
                'Member.dob' => $data['dob']
            ),
            'recursive' => -1
        ));
        
        $msg = "";
        
        if( $member ) {
            $member['MemberAccount'] = $account;
            $msg = "Existing member profile connected";
        }
        
        else {
            $member = array(
                'Member' => array(
                    'first_name' => $data['first_name'],
                    'last_name' => $data['last_name'],
                    'dob' => $data['dob'],
                    'gender' => $data['gender'],
                    'email' => $data['username']
                ),
                'MemberAccount' => $account
            );
            
            $msg = "New member profile created.";
        }
        
        $saved = $this->Member->saveAll($member);
        
        $account = $this->find('first', array(
            'conditions' => array('MemberAccount.username' => $data['username']),
            'order' => array('id' => 'desc'),
            'recursive' => -1
        ));
        
        if( $saved ) {
            return array('status' => 'success', 'message' => "Membership account created. $msg", 'account' => $account);
        }
        
        return array('status' => 'failed', 'message' => 'Account creation failed');
    }
    
    public function createTempAccount($member_id) {
        $member = $this->Member->find('first', array(
            'contain' => array(
                'MemberEmail' => array(
                    'limit' => 1
                )
            ),
            'conditions' => array(
                'Member.id' => $member_id
            )
        ));
        
        if( $member ) {
            $pass_salt = rand(10000,99999);
            $password = $this->gen_password();
            $password_hash = md5($password . $pass_salt);
            
            $data = $this->save(array(
                'member_id' => $member_id,
                'username' => $member['MemberEmail'][0]['email'],
                'password' => $password_hash,
                'pass_salt' => $pass_salt,
                'raw_password' => $password,
                'active' => 0,
                'reset_requested' => 1
            ));
            
            if($data) {
                return $data;
            }
            
            return false;
        }
        
        return false;
    }
    
    /**
     * 
     * @param string $username
     * @param string $auth_hash
     * @return array
     */
    public function activate($username, $auth_hash) {
        $account = $this->find('first', array(
            'contain' => array('Member'),
            'conditions' => array(
                'MemberAccount.username' => $username,
                'MemberAccount.active' => 0,
                'MemberAccount.deleted' => 0
            )
        ));
        
        if( $account ) {
            $checkHash = sha1($account['Member']['first_name'] .  $account['MemberAccount']['username'] . $account['Member']['last_name']);
            
            if( $checkHash == $auth_hash ) {
                $account['MemberAccount']['active'] = 1;
                
                $this->saveAll($account);
                
                // auto activate all organisation accounts so member can start managing the account immediately
                $this->OrganisationAccount->updateAll(array(
                    'OrganisationAccount.active' => 1
                ), array(
                    'OrganisationAccount.member_account_id' => $account['MemberAccount']['id'],
                    'OrganisationAccount.active' => 0
                ));
                
                return array('status' => 'success', 'message' => 'Member account activated successfully.', 'account' => $account);
            }
            
            else {
                return array('status' => 'failed', 'message' => 'Activation authentication failed.');
            }
        }
        
        return array('status' => 'failed', 'message' => 'Account activation failed');
    }
    
    public function changePassword($account_id, $new_pass) {
        $account = $this->find('first', array(
            'conditions' => array(
                'MemberAccount.id' => $account_id
            ),
            'recursive' => -1
        ));
        
        if( !$account ) {
            return array('status' => 'failed', 'message' => 'No account found for this process.');
        }
        
        /*$old_pass_hash = $account['MemberAccount']['password'];
        $check_hash = md5($old_pass . $account['MemberAccount']['pass_salt']);
        
        if( $old_pass_hash != $check_hash ) {
            return array('status' => 'failed', 'message' => 'Old password specified is incorrect. Please check and try again.', 
                'check_hash' => $check_hash, 'old_hash' => $old_pass_hash);
        }*/
        
        $new_pass_salt = rand(10000,99999);
        $new_pass_hash = md5($new_pass . $new_pass_salt);
        
        $saved = $this->save(array(
            'id' => $account_id,
            'member_id' => $account['MemberAccount']['member_id'],
            'username' => $account['MemberAccount']['username'],
            'password' => $new_pass_hash,
            'pass_salt' => $new_pass_salt,
            'reset_requested' => 0
        ));
        
        if( $saved ) {
            return array('status' => 'success', 'message' => 'Password change completed successfully.');
        }
        
        return array('status' => 'failed', 'message' => 'Password change process failed unexpectedly. Please try again later.');
    }
    
    private function gen_password($syllables = 3, $use_prefix = false) {

        // Define function unless it is already exists
        if (!function_exists('ae_arr')) {

            // This function returns random array element
            function ae_arr(&$arr) {
                return $arr[rand(0, sizeof($arr) - 1)];
            }

        }

        // 20 prefixes
        $prefix = array(
            '@ero', '@nti', 'aut0', '@ut0', 'Auto', 'b1', 'bio',
            'cine', 'decA', 'deCa', 'DecA', 'deMo', 'dYna', 'Eco',
            'ergo', 'eRgo', 'ge0', 'gyn0', 'gyno', 'hypo', 'hypa', 'Hypo', 'hYpo', 'kilo',
            'mega', 'tera', 'terA', 'ter@', 'Ter@', 'mini', 'n@no', 'nano', 'duo', 'du0', 'Duo',
            'dir', 'san', 'diego', '1n',
        );

        // 10 random suffixes
        $suffix = array(
            'dom', 'ity', 'ment', 'sion', 'ness',
            'ence', 'er', 'ist', 'tion', 'or',
            'd0m', 'itY', 'Ment', 'sioN', 'n3ss',
            'Ence', 'eR', '1st', 'ti0n', '@r'
        );

        // 8 vowel sounds
        $vowels = array('a', 'o', 'e', 'i', 'y', 'u', 'ou', 'oo');

        // 20 random consonants
        $consonants = array('w', 'r', 't', 'p', 's', 'd', 'f', 'g', 'h', 'j',
            'k', 'l', 'z', 'x', 'c', 'v', 'b', 'n', 'm', 'qu');

        $password = $use_prefix ? ae_arr($prefix) : '';
        $password_suffix = ae_arr($suffix);

        for ($i = 0; $i < $syllables; $i++) {
            // selecting random consonant
            $doubles = array('n', 'm', 't', 's');
            $c = ae_arr($consonants);
            if (in_array($c, $doubles) && ($i != 0)) { // maybe double it
                if (rand(0, 2) == 1) // 33% probability
                    $c .= $c;
            }
            $password .= $c;
            // selecting random vowel
            $password .= ae_arr($vowels);

            if ($i == $syllables - 1) // if suffix begin with vovel
                if (in_array($password_suffix[0], $vowels)) // add one more consonant
                    $password .= ae_arr($consonants);
        }

        // selecting random suffix
        $password .= $password_suffix;

        return $password;
    }
}
